package com.example.selfservicestation.mob

import com.example.selfservicestation.event.ConfigEvent
import com.example.selfservicestation.util.Crc8
import com.example.selfservicestation.util.Executor
import com.example.selfservicestation.util.Logger
import com.example.selfservicestation.util.jvmSeconds
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.LinkedBlockingQueue

/**
 * modbus 最小发送间隔（80豪秒）
 */
const val MODBUS_MIN_SEND_INTERVAL = 1000_000_000

/**
 * 最大从机数量
 */
const val MODBUS_MAX_SLAVE_COUNT = 16

/**
 * 从机状态：未变化
 */
const val MODBUS_SLAVE_STATUS__NO_CHANGE = -1

/**
 * 从机状态：已经满溢
 */
const val MODBUS_SLAVE_STATUS__FULL = 0b00100000

/**
 * 从机状态：满溢百分比
 */
const val MODBUS_SLAVE_STATUS__FULL_PERCENT = 0b00100000

/**
 * 从机状态：烟雾报警
 * */
const val MODBUS_SLAVE_STATUS__SMOKE = 0b01000000

/**
 * 从机状态：是否已完全关闭
 */
const val MODBUS_SLAVE_STATUS__DOOR_CLOSED = 0b00000001

/**
 * 从机状态：是否已完全开启
 */
const val MODBUS_SLAVE_STATUS__DOOR_OPENED = 0b00000010

/**
 * 从机状态：开门按钮是否被按下
 */
const val MODBUS_SLAVE_STATUS__OPEN_BUTTON_PRESSED = 0b00010000

/**
 * 从机状态：是否有扫码头事件（刷卡|扫码）
 */
const val MODBUS_SLAVE_STATUS__SCANNER = 0b00001000

/**
 * 从机状态：托盘关到位
 */
const val MODBUS_SLAVE_STATUS__PALLET_CLOSED = 0b00001000

/**
 * 从机状态：托盘开到位
 */
const val MODBUS_SLAVE_STATUS__PALLET_OPENED = 0b00010000

/**
 * 从机状态：清运门状态
 */
const val  MODBUS_SLAVE_STATUS__CLEANING_DOOR_STATUS = 0b00100000_00000000

/**
 * 从机状态：防夹手
 */
const val  MODBUS_SLAVE_STATUS__ANTI_PINCH_HAND = 0b00000000_10000000

/**
 * 从机状态：两阶段关门
 */
const val  MODBUS_SLAVE_STATUS__CLOSE_DOOR_WITHIN_TWO_STAGE = 0b01000000_00000000

/**
 * 从机状态：是否有开门按钮事件
 */
const val MODBUS_SLAVE_STATUS__OPEN_DOOR_BY_PHYSICAL_BUTTON: Byte = 0b00010000

/**
 * 从机状态：是否允许无授权开门
 */
const val MODBUS_SLAVE_CTRL__ALLOW_OPEN_DOOR_UNIDENTIFIED: Byte = 0b01000000

/**
 * 从机状态：是否感应到人体
 */
const val MODBUS_SLAVE_STATUS__BODY_NEAR = 0b00000100


/**
 * ModBus 协议管理器
 */
class ModBusManager(
    // 从机数量
    var slaveCount: Int,
    // 串口管理器
    val portManager: PortManager,
    // 是否输出详细信息
    val verbose: Boolean = false
) {

    @Volatile
    private var running = true

    /**
     * 正在发送的任务
     */
    @Volatile
    private var sending: FrameTask? = null

    /**
     * 等待发送队列
     */
    private val pendings = LinkedBlockingQueue<FrameTask>()

    /**
     * 线程锁
     */
    private val lock = Any()


    private var thread: Thread? = null


    init {
        portManager.listen { res ->
            if (verbose) {
                Logger.i("ModBus", "接收：${res.toHexString()}--${sending == null}")
            }
                sending?.run {
                    Logger.i("ModBus", "响应: ${res.toHexString()}匹配, running：${running}")
                    if (match(res) && running) {
                        done?.let { it(res) }
                        Logger.i("ModBus", "设置为null--1}")
                        sending = null
                    } else {
                        Logger.i("ModBus", "设置为null--2}")
                        sending = null
                        Logger.i("ModBus", "响应: ${res.toHexString()}未匹配, running：${running}")
                    }
                }
        }
    }

    /**
     * 发送队列的消息
     */
    private fun takePendingToSend() {
        if (sending == null) {
            sending = pendings.take()
        }
        if (!running) {
            return
        }
        sending?.run {
            waitIfNecessary()
        }
//        synchronized(lock) {
            sending?.run {

                if (shouldSend()) {
                    if (hasSend) return
                    if (portManager.send(req)) {
                        afterSent()
                        hasSend = true
                        Logger.i("ModBus", "发送：${req.toHexString()}")
                    } else {
                        Logger.e("ModBusManager","无法与主控板通讯")
                    }
                } else {
                    Logger.i("ModBus", "未响应: ${req.toHexString()}")
                    // 放弃处理，回调空数据
                    done?.let { it(byteArrayOf()) }
                    Logger.i("ModBus", "设置为null--3}")
                    sending = null
                    onFrameTimeout()
                }
            }
//        }
    }

    private var timeouts = 0
    private var lastTimeout = 0L

    private fun onFrameTimeout() {
        val now = jvmSeconds()
        if (now - lastTimeout > 10) {
            timeouts = 0
        }
        // 如果连续超时达到 15 次，则重建 Modbus 连接
        if (++timeouts > 15) {
            EventBus.getDefault().post(ConfigEvent())
        }
        lastTimeout = now
    }

    /**
     * 循环发送给所有从机
     * @param frame 发送报文
     * @param listener 每轮发送完后的数据监听
     * @param delayMills 每轮发送的间隔
     */
    fun repeatSendToAll(
        frame: MBFrame,
        interrupt: (() -> List<Boolean>)? = null,
        listener: (res: List<ByteArray>) -> Unit,
        delayMills: Long
    ): ModBusManager {
        val keep = interrupt?.invoke()?.run { !this[0] } ?: false
        if (keep) {
            sendToAll(frame) {
                if (running) {
                    listener(it)
                    Executor.delayOnIO({
                        if (running) {
                            repeatSendToAll(frame, interrupt, listener, delayMills)
                        }
                    }, delayMills)
                }
            }
        } else {
            Executor.delayOnIO({
                if (running) {
                    repeatSendToAll(frame, interrupt, listener, delayMills)
                }
            }, delayMills)
        }
        return this
    }


    /**
     * 发送给所有从机
     * @param frame 发送报文
     * @param done 所有从机都发送完成后的回调
     */
    fun sendToAll(frame: MBFrame, done: ((res: List<ByteArray>) -> Unit)? = null) {
        if (slaveCount == 0) {
            done?.let { it(listOf()) }
            return
        }
        sendUp(0, frame, done, ArrayList())
    }

    private fun sendUp(
        index: Int,
        frame: MBFrame,
        done: ((res: List<ByteArray>) -> Unit)?,
        resList: ArrayList<ByteArray>
    ) {
        sendTo(index, frame) { res ->
            resList.add(res)
            if (index >= slaveCount - 1) {
                // 已经发送完
                if (running) {
                    done?.let { it(resList) }
                }
            } else {
                // 发送给下一个从机
                sendUp(index + 1, frame, done, resList)
            }
        }
    }

    /**
     * 发送给序号为 index 的从机
     * @param index 从机序号
     * @param frame 发送报文
     * @param done 完成回调
     */
    fun sendTo(
        index: Int,
        frame: MBFrame,
        allowRetransmission: Boolean = false,
        minSendIntervalNanoSeconds: Int = MODBUS_MIN_SEND_INTERVAL,
        done: ((res: ByteArray) -> Unit)? = null
    ) {
        if (slaveCount <= 0) {
            Logger.i("ModBus", "sendTo($index), slaveCount为0, 返回空数据")
            done?.invoke(byteArrayOf())
            return
        }
        if (index < 0 || index >= slaveCount) {
            throw IllegalArgumentException("index [${index}] out of bound [${slaveCount}]")
        }
        val task = FrameTask(frame.compile(), done)
        task.allowRetransmission = allowRetransmission
        task.minSendInterval = minSendIntervalNanoSeconds
        pendings.add(task)
    }

    fun start() {
        Logger.i(TAG, "start()")
        running = true
        thread = Thread {
            while (running) {
                try {
                    takePendingToSend()
                } catch (_: InterruptedException) {
                }
            }
        }
        thread?.isDaemon = true
        thread?.start()
    }

    fun isRunning(): Boolean {
        return running
    }

    fun stop() {
        Logger.i(TAG, "stop()")
        running = false
        thread?.interrupt()
        portManager.close()
    }

    companion object {
        private const val TAG = "ModBus_Mgr"
    }

}


class FrameTask(
    val req: ByteArray,
    val done: ((res: ByteArray) -> Unit)?,    // 响应回调
    var hasSend: Boolean = false
) {

    /**
     * 是否允许重发
     */
    var allowRetransmission: Boolean = false

    /**
     * 上次发送时间
     */
    var lastSent: Long = 0

    /**
     * 已发送次数
     */
    var sentCount = 0

    /**
     * 最小发送间隔
     */
    var minSendInterval: Int = MODBUS_MIN_SEND_INTERVAL


    fun waitIfNecessary() {
        val interval = System.nanoTime() - lastSent
        if (interval < minSendInterval) {
            Thread.sleep((minSendInterval - interval) / 1000_000)
        }
    }

    fun shouldSend(): Boolean {
        return if (allowRetransmission) {
            sentCount < 3
        } else {
            sentCount < 1
        }
    }

    fun afterSent() {
        sentCount++
        lastSent = System.nanoTime()
    }

    /**
     * 判断 res 是否是 frame 的响应
     */
    fun match(res: ByteArray): Boolean {
        // 从机地址 和 功能码 必须相同
        if (res.size < 5 || req[4] != res[4]) {
            return false
        }
        // 报文2 的 CRC校验得正确
//        val crc8 = Crc8.crc8(res.copyOfRange(0,res.size - 1))
//        return crc8 == res[res.size - 1].toInt()
    return true
    }

}

private const val FRAME_TYPE_READ: Byte = 0xAA.toByte()
private const val FRAME_TYPE_WRITE: Byte = 0x06
private const val FRAME_TYPE_SCANNER: Byte = 0x43

private val TURN_ON: ByteArray = byteArrayOf(0x50)
private val QUERY_STATE: ByteArray = byteArrayOf(0x51)
private val CHECK_COUNT: ByteArray = byteArrayOf(0xF3.toByte())
private val PROTOCOL_HEADER = byteArrayOf(0xaa.toByte(),0x55)
private val BOARD_ADDRESS: ByteArray =  byteArrayOf(0)

/**
 * ModBus 数据帧
 */
class MBFrame(

    val lockIndex: Int //index为,-1是检测锁状态, -2 是检查锁路数
) {

    /**
     * @param index 从机序号
     */
    fun compile(): ByteArray {
        val dataCount = when(lockIndex){
            -1,-2 -> byteArrayOf(0x2)
            else -> byteArrayOf(0x3)
        }
        val command = when(lockIndex) {
            -1 -> QUERY_STATE
            -2 -> CHECK_COUNT
            else -> TURN_ON
        }
        val lockIndexByteArray = when(lockIndex){
            -1, -2 -> byteArrayOf()
            else -> byteArrayOf(lockIndex.toByte())
        }


//        val dataCount = if (lockIndex != null) byteArrayOf(0x3) else byteArrayOf(0x2)
//        val command = if (lockIndex != null) TURN_ON else QUERY_STATE
//        val lockIndexByteArray = if (lockIndex != null) byteArrayOf(lockIndex.toByte()) else byteArrayOf()
        val dataByteArray = PROTOCOL_HEADER + dataCount + BOARD_ADDRESS + command + lockIndexByteArray
//        val crc8 = Crc8.crc8(dataByteArray)
        val crc8 =  when(lockIndex){
            -1 -> 0x1D
            -2 -> 0x0E
            else -> Crc8.crc8(dataByteArray)
        }
        return dataByteArray + byteArrayOf(crc8.toByte())
    }

    companion object {


    }

}